namespace FIREFLYX {
    /** 顶点着色器 Attributes 布局位置值 */
    export enum AttributeLayout {
        /** 顶点坐标 */
        POSITION = 0,
        /** 法线坐标 */
        NORMAL = 1,
        /** 切线坐标 */
        TANGENT = 2,
        /** 纹理坐标0 */
        TEXCOORD_0 = 3,
        /** 颜色坐标0 */
        COLOR_0 = 4,
        /** 骨骼关节坐标0 */
        JOINTS_0 = 5,
        /** 骨骼关节权重坐标0 */
        WEIGHTS_0 = 6,
        /** 纹理坐标1 */
        TEXCOORD_1 = 7,
        /** 颜色坐标1 */
        COLOR_1 = 8,
        /** 纹理坐标2 */
        TEXCOORD_2 = 9,

    }

    /** 网格数据 */
    export class MeshData {
        /**顶点位置坐标数组 */
        public posArr: Nullable<Float32Array>;
        /**索引数组 */
        public triIndexArr: Nullable<Uint32Array | Uint16Array | Uint8Array>;
        /**纹理UV坐标数组 */
        public uvArr: Nullable<Float32Array>;
        /**纹理UV1坐标数组 */
        public uvArr1: Nullable<Float32Array>;
        /**纹理UV2坐标数组 */
        public uvArr2: Nullable<Float32Array>;
        /**顶点法线坐标数组 */
        public normalArr: Nullable<Float32Array>;
        /**顶点切线坐标数组 */
        public tangentArr: Nullable<Float32Array>;
        /** 位置坐标的最小值 */
        public posMin: Vector3 = new Vector3();
        /** 位置坐标的最大值 */
        public posMax: Vector3 = new Vector3();
        /** 计算位置坐标的 最大与最小值 */
        public calcPosBoundary() {
            if (!this.posArr) return;
            let minX = Number.POSITIVE_INFINITY;
            let minY = Number.POSITIVE_INFINITY;
            let minZ = Number.POSITIVE_INFINITY;
            let maxX = Number.NEGATIVE_INFINITY;
            let maxY = Number.NEGATIVE_INFINITY;
            let maxZ = Number.NEGATIVE_INFINITY;
            for (let i = 0, len = this.posArr.length; i < len; i += 3) {
                let x = this.posArr[i];
                let y = this.posArr[i + 1];
                let z = this.posArr[i + 2];
                minX = Math.min(minX, x);
                minY = Math.min(minX, y);
                minZ = Math.min(minX, z);
                maxX = Math.max(maxX, x);
                maxY = Math.max(maxY, x);
                maxZ = Math.max(maxZ, x);
            }
            this.posMin.set(minX, minY, minZ);
            this.posMax.set(maxX, maxY, maxZ);
        }

        /**
         * 获取 制定三角形顶点索引数组的 类型
         * @param array 三角形顶点索引数组
         * @returns [Uint32Array | Uint16Array | Uint8Array]
         */
        public static GetTriIndexBufferTypeByPosLen(positionCount: number) {
            if (positionCount == null) return Uint32Array;
            const u16Max = 65535;
            const u8Max = 255;
            return positionCount > u16Max ? Uint32Array : positionCount > u8Max ? Uint16Array : Uint8Array;
        }
    }

    /** 网格的部分基本单元 */
    export class MeshPrimitive {
        /** 材质的索引 */
        public material: number;
        /** mesh顶点或索引 数组中的开始位置 */
        public start: number;
        /** mesh顶点或索引 buffer中的开始位置(byte为单位) */
        public bufferStart: number;
        /** mesh顶点或索引 数组中的长度 */
        public length: number;
        /** mesh 的draw模式 webgl的常量值 POINTS,LINES,TRIANGLES... */
        public mode: number;
        /** mesh 的正面连接顺序 webgl的常量值 CW 顺时针,CCW 逆时针*/
        public frontFace: number;
    }

    /** 模型网格资源 */
    export class Mesh extends Asset {
        /** element 使用 webgl UNSIGNED_INT 类型标识*/
        public static readonly UNSIGNED_INT = 5125;
        /** element 使用 webgl UNSIGNED_SHORT 类型标识*/
        public static readonly UNSIGNED_SHORT = 5123;
        /** element 使用 webgl UNSIGNED_BYTE 类型标识*/
        public static readonly UNSIGNED_BYTE = 5121;
        /** element 空类型 */
        public static readonly ELEMENT_NULL_TYPE = -1;
        private _VAO: Nullable<WebGLVertexArrayObject>;
        private _glPosBuffer: Nullable<WebGLBuffer>;
        private _glUVBuffer: Nullable<WebGLBuffer>;
        private _glUV1Buffer: Nullable<WebGLBuffer>;
        private _glUV2Buffer: Nullable<WebGLBuffer>;
        private _glNormalBuffer: Nullable<WebGLBuffer>;
        private _glTangentBuffer: Nullable<WebGLBuffer>;
        private _glIndexBuffer: Nullable<WebGLBuffer>;
        private _elementLength: number = 0;
        private _elementType: 5125 | 5123 | 5121 | -1 = -1;
        /** 网格部分基本单元列表 */
        public primitives: MeshPrimitive[] = [];
        /** 网格数据 */
        public data: Nullable<MeshData>;
        /** webgl 的顶点缓冲区对象 */
        public get glPosBuffer() { return this._glPosBuffer; }
        /** webgl 的UV缓冲区对象 */
        public get glUVBuffer() { return this._glUVBuffer; }
        /** webgl 的法线缓冲区对象 */
        public get glNormalBuffer() { return this._glNormalBuffer; }
        /** webgl 的切线缓冲区对象 */
        public get glTangentBuffer() { return this._glTangentBuffer; }
        /** webgl 的顶点索引缓冲区对象 */
        public get glIndexBuffer() { return this._glIndexBuffer; }
        /** webgl 的顶点索引数量 */
        public get elementLenght() { return this._elementLength; }
        /** webgl 的顶点索引元素类型常量 , -1 表示不使用 顶点索引*/
        public get elementType() { return this._elementType; }
        /** webgl VAO 对象 */
        public get VAO() { return this._VAO; }

        /** 提交使用当前网格数据 */
        public apply() {
            let mData = this.data;
            if (!mData) {
                console.warn(`apply fial , data is null.`);
                return;
            }
            let gl = App.webgl2;
            if (!gl) {
                console.warn(`apply fial , gl is null.`);
                return;
            }
            if (!mData.posArr || mData.posArr.length < 1) {
                console.warn(`apply fial , vertex buffer is empty.`);
                return;
            }

            //设置顶点数据
            let setGLData = (glBuffer: Nullable<WebGLBuffer>, data: BufferSource, target: number = gl.ARRAY_BUFFER, usage: number = gl.STATIC_DRAW) => {
                if (data.byteLength <= 0) {
                    //data invalid
                    if (glBuffer != null) gl.deleteBuffer(glBuffer);    //clear buffer
                    return null;
                }
                if (glBuffer == null) glBuffer = gl.createBuffer();
                gl.bindBuffer(target, glBuffer);
                gl.bufferData(target, data, usage);            //data set to webglBuffer
                return glBuffer;
            };
            //绑定激活Attrib
            let setPointer = (aLayout: AttributeLayout, size: number, type: GLenum = gl.FLOAT) => {
                gl.vertexAttribPointer(aLayout, size, type, false, 0, 0);  //数据不够时会填充默认值
                //连接启用  分配个 变量的 缓冲区对象
                gl.enableVertexAttribArray(aLayout);
            };

            //VAO 模式
            //create vao
            this._VAO = gl.createVertexArray();
            //bind VAO , 记录VBO 的操作
            gl.bindVertexArray(this._VAO);

            //pos
            this._glPosBuffer = setGLData(this._glPosBuffer, mData.posArr);
            setPointer(AttributeLayout.POSITION, 3);
            //uv
            if (mData.uvArr) {
                this._glUVBuffer = setGLData(this._glUVBuffer, mData.uvArr);
                setPointer(AttributeLayout.TEXCOORD_0, 2);
            }
            //uv1
            if (mData.uvArr1) {
                this._glUV1Buffer = setGLData(this._glUV1Buffer, mData.uvArr1);
                setPointer(AttributeLayout.TEXCOORD_1, 2);
            }
            //uv2
            if (mData.uvArr2) {
                this._glUV2Buffer = setGLData(this._glUV2Buffer, mData.uvArr2);
                setPointer(AttributeLayout.TEXCOORD_2, 2);
            }

            //normal
            if (mData.normalArr) {
                this._glNormalBuffer = setGLData(this._glNormalBuffer, mData.normalArr);
                setPointer(AttributeLayout.NORMAL, 3);
            }

            //tangent
            if (mData.tangentArr) {
                this._glTangentBuffer = setGLData(this._glTangentBuffer, mData.tangentArr);
                setPointer(AttributeLayout.TANGENT, 4);
            }

            //index
            if (mData.triIndexArr) {
                let idxD = mData.triIndexArr;
                this._glIndexBuffer = setGLData(this._glIndexBuffer, idxD, gl.ELEMENT_ARRAY_BUFFER);
                //elem length
                this._elementLength = idxD.length;
            } else {
                this._elementLength = 0;
            }
            //index elemnet type
            this.setTriIndexType();

            //检查添加默认Primitive
            if (this.primitives.length <= 0) {
                this.setDefPrimitive();
            } else {
                //更新meshPrimitive length
                const mp = this.primitives[this.primitives.length - 1];
                this.adjustPrimitiveLen(mp);
            }

            //clear
            gl.bindBuffer(gl.ARRAY_BUFFER, null);
            // gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, null);
            gl.bindVertexArray(null);

        }

        /**
         * 设置调整 MeshPrimitive 的length
         * @param mp MeshPrimitive对象
         */
        private adjustPrimitiveLen(mp: MeshPrimitive) {
            let allLen = 0;
            if (this._elementLength > 0) {
                allLen = this._elementLength;
            } else {
                if (this.data?.posArr) {
                    allLen = (this.data?.posArr.length / 3) || 0;
                }
            }
            mp.length = allLen - mp.start;
            if (mp.length < 0) mp.length = 0;
        }

        //补全默认Primitive设置
        private setDefPrimitive() {
            let p = new MeshPrimitive();
            p.material = 0;
            p.start = 0;
            p.mode = App.webgl2.TRIANGLES;
            p.frontFace = App.webgl2.CCW;
            this.adjustPrimitiveLen(p);
            this.primitives.push(p);
        }

        /** 设置 顶点索引元素类型常量*/
        private setTriIndexType() {
            let d = this.data;
            this._elementType = -1;
            if (!d || !d.triIndexArr) return;
            let byteSize = d.triIndexArr.byteLength / d.triIndexArr.length;
            this._elementType = byteSize == 4 ? Mesh.UNSIGNED_INT : byteSize == 2 ? Mesh.UNSIGNED_SHORT : Mesh.UNSIGNED_BYTE;
        }
    }
}